#include	"response.h"
#include	"utils/logger.h"
#include	"utils/utils.h"

#include	<microhttpd.h>
#include	<fcntl.h>

#if defined(_WIN32)
#	include	<io.h>
#	pragma	warning(disable:4996)
#else
#	include	<unistd.h>
#	include	<sys/stat.h>
#endif

void Response::Send() {
	MHD_Response * pRsp = MHD_create_response_from_buffer(
		_sContent.size(),
		(void *)_sContent.c_str(),
		MHD_RESPMEM_MUST_COPY);

	if (!pRsp) {
		GLog.Error("Failed to create response!");
		return;
	}

	for (auto & kv : _mHeader) MHD_add_response_header(pRsp, kv.first.c_str(), kv.second.c_str());

	if (MHD_queue_response(_pConn, _nCode, pRsp) != MHD_YES) {
		GLog.Error("Failed to send response!");
		return;
	}

	MHD_destroy_response(pRsp);

	_nCode = 200;
	_mHeader.clear();
	_sContent.clear();
}

void Response::Error(int nCode) {
	_nCode		= nCode;
	_sContent	= "<div style=\"text-align: center; margin-top: 20%\"><p style=\"font-family: Courier; font-size: 4em;\"><label>:( " + std::to_string(nCode);
	
	switch (nCode) {
	case MHD_HTTP_BAD_REQUEST: _sContent.append(" BAD REQUEST"); break;
	case MHD_HTTP_FORBIDDEN: _sContent.append(" FORBIDDEN"); break;
	case MHD_HTTP_NOT_FOUND: _sContent.append(" NOT FOUND"); break;
	case MHD_HTTP_METHOD_NOT_ALLOWED: _sContent.append(" METHOD NOT ALLOWED"); break;
	case MHD_HTTP_REQUEST_TIMEOUT: _sContent.append(" REQUEST TIME OUT"); break;
	case MHD_HTTP_INTERNAL_SERVER_ERROR: _sContent.append(" INTERNAL SERVER ERROR"); break;
	case MHD_HTTP_SERVICE_UNAVAILABLE: _sContent.append(" SERVICE UNVALIBLE"); break;
	default: _sContent.append(" SERVER ERROR"); break;
	}
	
	_sContent.append(" </label><hr><br></p></div>");
	Header(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
}

void Response::Redirect(const std::string & sUrl) {
	Header(MHD_HTTP_HEADER_LOCATION, sUrl.c_str());
	Header(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
	Header(MHD_HTTP_HEADER_CACHE_CONTROL, "no-cache");
	_nCode = 302;
	_sContent = "";
}

void Response::File(const std::string & sUrl) {
	std::string sFile = "." + sUrl;
	int nFd = -1;
	struct stat iInfo;

	if (!Exists(sFile.c_str())) {
		Error(404);
		Send();
	} else if ((nFd = open(sFile.c_str(), O_RDONLY)) == -1 || 0 != fstat(nFd, &iInfo) || !(iInfo.st_mode & S_IFREG)) {
		Error(403);
		Send();
	} else {
		MHD_Response * pRsp = MHD_create_response_from_fd64(iInfo.st_size, nFd);
		std::string sModify = GMTime((uint32_t)iInfo.st_mtime);
		int nCode = 200;

		if (!pRsp) {
			GLog.Error("Failed to send file : %s!", sUrl.c_str());
			return;
		}
		
		MHD_add_response_header(pRsp, MHD_HTTP_HEADER_LAST_MODIFIED, sModify.c_str());

		const char * pModify = MHD_lookup_connection_value(_pConn, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_MODIFIED_SINCE);
		if (!pModify || sModify != pModify) {
			nCode = 200;

			std::string sMIME = "application/octet-stream";
			std::string	sExt = sFile.substr(sFile.find_last_of('.') + 1);
			if (sExt == "js") sMIME = "text/javascript";
			else if (sExt == "css") sMIME = "text/css";
			else if (sExt == "htm" || sExt == "html") sMIME = "text/html";
			else if (sExt == "txt") sMIME = "text/plain";
			else if (sExt == "jpg" || sExt == "jpeg") sMIME = "image/jpeg";
			else if (sExt == "gif") sMIME = "image/gif";
			else if (sExt == "png") sMIME = "image/png";
			else if (sExt == "bmp") sMIME = "image/bmp";
			else if (sExt == "ico") sMIME = "image/x-ico";
			else if (sExt == "svg") sMIME = "image/svg+xml";
			else if (sExt == "tif" || sExt == "tiff") sMIME = "image/tiff";
			else if (sExt == "swf") sMIME = "application/x-shockwave-flash";

			MHD_add_response_header(pRsp, MHD_HTTP_HEADER_CONTENT_TYPE, sMIME.c_str());
			MHD_add_response_header(pRsp, MHD_HTTP_HEADER_CACHE_CONTROL, "private, max-age=30");
		} else {
			nCode = 304;
		}

		if (MHD_queue_response(_pConn, nCode, pRsp) != MHD_YES)
			GLog.Error("Failed to send file : %s!", sUrl.c_str());

		MHD_destroy_response(pRsp);
	}
}
